﻿//  Copyright (c) 2011 Ray Liang (http://www.dotnetage.com)
//  Dual licensed under the MIT and GPL licenses:
//  http://www.opensource.org/licenses/mit-license.php
//  http://www.gnu.org/licenses/gpl.html

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Collections;
using System.Web.UI;

namespace DNA.Mvc.jQuery
{
    //public class GridBuilder : jQueryComponentBuilder<GridOptions, Grid, GridBuilder>
    //{
    //    private int pageIndex = 1;
    //    private int pageSize = 20;

    //    protected override string jQueryPluginName
    //    {
    //        get { return "dgrid"; }
    //    }

    //    public GridBuilder(Grid component, AjaxHelper helper) : base(component, helper) { }

    //    public GridBuilder Header(Action content)
    //    {
    //        if (content != null)
    //            Component.Header = content;
    //        return this;
    //    }

    //    public GridBuilder Sortable()
    //    {
    //        Options(opts =>
    //        {
    //            opts.Sortable = true;
    //            var sortHandler = new StringBuilder();

    //        });
    //        return this;
    //    }

    //    public GridBuilder Pagable()
    //    {
    //        return this.Pagable(pageIndex, pageSize);
    //    }

    //    public GridBuilder Pagable(int index)
    //    {
    //        return this.Pagable(index, pageSize);
    //    }

    //    public GridBuilder Pagable(int index, int size)
    //    {
    //        if (index <= 0)
    //            throw new ArgumentOutOfRangeException("index must be > 0");
    //        if (size <= 0)
    //            throw new ArgumentOutOfRangeException("size must be >0");

    //        pageIndex = index;
    //        pageSize = size;
    //        var url = new UrlHelper(Helper.ViewContext.RequestContext);
    //        Component.Footer = new Action(() =>
    //        {
    //            Helper.ViewContext.Writer.Write("<div id=\"" + Component.Id + "_pager\"></div>");
    //        });
    //        return this;
    //    }

    //    public virtual GridBuilder AutoGenerateColumns(Type modelType)
    //    {
    //        Component.AutoGenerateColumns = true;
    //        //if (value)
    //        //{
    //        Component.Columns.Clear();
    //        //var enumer = Component.Model.GetEnumerator();

    //        //Type typeTest = null;
    //        //if (enumer.MoveNext()) typeTest = enumer.Current.GetType();

    //        //Type modelType = typeTest == null ? null : typeTest.GetType();
    //        if (modelType != null)
    //        {
    //            var properties = modelType.GetProperties();
    //            foreach (var property in properties)
    //            {
    //                if (property.CanRead)
    //                {
    //                    var col = new GridColumn() { Title = property.Name, DataField = property.Name, DataType = property.PropertyType.ToString() };
    //                    col.Width = 100;
    //                    Component.AddItem(col);
    //                }
    //            }
    //        }
    //        //}
    //        return this;
    //    }

    //    public GridBuilder ShowHorizontalScrollBar()
    //    {
    //        Component.ShowHorizontalScrollBar = true;
    //        return this;
    //    }

    //    private string _httpMethod = "POST";
    //    private string _url = "";

    //    public GridBuilder Bind(string url)
    //    {
    //        return this.Bind(url, _httpMethod);
    //    }

    //    public GridBuilder Bind(string url, string httpMethod)
    //    {
    //        if (!string.IsNullOrEmpty(httpMethod))
    //            _httpMethod = httpMethod;
    //        _url = url;
    //        return this;
    //    }

    //    public GridBuilder Columns(Action<GridColumnBuilderFactory> columns)
    //    {
    //        var factory = new GridColumnBuilderFactory(Component, Helper);
    //        columns.Invoke(factory);
    //        return this;
    //    }

    //    public GridBuilder RowBody(Action<object> body)
    //    {
    //        Component.RowTemplate = body;
    //        return this;
    //    }

    //    protected void RenderClientTemplate(HtmlTextWriter writer)
    //    {
    //        writer.WriteBeginTag("script");
    //        writer.WriteAttribute("id", Component.Id + "_tmpl");
    //        writer.WriteAttribute("type", "text/x-jquery-tmpl");
    //        writer.Write(HtmlTextWriter.TagRightChar);
    //        writer.WriteBeginTag("tr");
    //        writer.WriteAttribute("class", "d-grid-row");
    //        writer.Write(HtmlTextWriter.TagRightChar);
    //        foreach (var col in Component.Columns)
    //        {
    //            writer.WriteBeginTag("td");
    //            writer.WriteAttribute("class", "d-grid-cell " + col.Id.ToString());
    //            if (col.Width > 0)
    //                writer.WriteAttribute("style", "width:" + col.Width.ToString() + "px;");
    //            writer.Write(HtmlTextWriter.TagRightChar);
    //            if (col.CellTemplate == null)
    //                writer.Write("${" + col.DataField + "}");
    //            else
    //                col.CellTemplate.Invoke();
    //            writer.WriteEndTag("td");
    //        }

    //        writer.WriteEndTag("tr");
    //        writer.WriteEndTag("script");
    //    }

    //    public override void Render()
    //    {
    //        base.Render();
    //        if (!string.IsNullOrEmpty(_url) && (Component.Model == null))
    //        {
    //            using (var writer = new HtmlTextWriter(Helper.ViewContext.Writer))
    //            {
    //                RenderClientTemplate(writer);
    //            }
    //            var startup = new StringBuilder();
    //            startup.Append(jQuerySelector)
    //                .Append(".dgrid(\"load\",\"")
    //                .Append(_url)
    //                .Append("\",{index:")
    //                .Append(pageIndex.ToString())
    //                .Append(",size:")
    //                .Append(pageSize.ToString())
    //                .Append("},\"")
    //                .Append(Component.Id)
    //                .Append("_tmpl\",function(data){")
    //                .Append("$(\"#")
    //                .Append(Component.Id)
    //                .Append("_pager\").pager({")
    //                .Append("pageindex:")
    //                 .Append(pageIndex.ToString())
    //                 .Append(",pagesize:")
    //                 .Append(pageSize.ToString())
    //                .Append(",totalrecords:data.Total,changed:function(event,ui){")
    //                .Append(jQuerySelector)
    //                .Append(".dgrid(\"load\",\"")
    //                .Append(_url)
    //                .Append("\",{index:ui.pageIndex,size:ui.pageSize},\"")
    //                 .Append(Component.Id)
    //                .Append("_tmpl\");")
    //                .Append("}")
    //                .Append("});")
    //                .Append("});");
    //            Helper.RegisterStartupScript(startup.ToString());
    //        }
    //    }
    //}

    public class GridBuilder<TModel> : jQueryComponentBuilder<GridOptions, Grid, GridBuilder<TModel>>
        where TModel : class
    {
        private string[] groups = null;
        private int _index = 1;
        private int _size = 10;
        private string _httpMethod = "POST";
        private string _url = "";
        private bool isClientMode = false;
        private string filter = "";

        protected override string jQueryPluginName
        {
            get { return "dgrid"; }
        }

        public GridBuilder(Grid component, ViewContext viewContext, IViewDataContainer viewContainer) : base(component, viewContext, viewContainer) { }

        /// <summary>
        /// Specified the header template.
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        public GridBuilder<TModel> Header(Action content)
        {
            if (content != null)
                Component.Header.Content = content;
            return this;
        }

        /// <summary>
        /// Specified the header inline block template.
        /// </summary>
        /// <param name="template"></param>
        /// <returns></returns>
        public GridBuilder<TModel> Header(Func<object,object> template)
        {
            if (template != null)
                Component.Header.InlineContent = template;
            return this;
        }
        /// <summary>
        /// Allows the Grid could paging data.
        /// </summary>
        /// <returns></returns>
        public GridBuilder<TModel> Pagable()
        {
            return this.Pagable(1, 20);
        }

        /// <summary>
        /// Build pager for data grid and enable paging data feature.
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public GridBuilder<TModel> Pagable(int index)
        {
            return this.Pagable(index, 20);
        }

        public GridBuilder<TModel> Pagable(int index, int size)
        {
            if (index > 0)
                _index = index;
            if (size > 0)
                _size = size;
            Component.Pagable = true;
            //BuildPager(index, size);
            return this;
        }

        /// <summary>
        /// Enable sortable feature
        /// </summary>
        /// <returns></returns>
        public GridBuilder<TModel> Sortable()
        {
            Component.Sortable = true;
            return this;
        }

        /// <summary>
        /// Enable sortable feature and set sorting columns when grid init.
        /// </summary>
        /// <param name="columns"></param>
        /// <returns></returns>
        public GridBuilder<TModel> Sortable(Action<GridOrderedColumnRefFactory<TModel>> columns)
        {
            var factory = new GridOrderedColumnRefFactory<TModel>(Component);
            columns.Invoke(factory);
            return this.Sortable();
        }

        /// <summary>
        /// Enable fiterable feature that shows filter input on every visiable column
        /// </summary>
        /// <returns></returns>
        public GridBuilder<TModel> Filterable()
        {
            Component.Filterable = true;
            return this;
        }

        /// <summary>
        /// Enable filterable feature that shows filter input on every visiable column and specified the init filters.
        /// </summary>
        /// <param name="filters"></param>
        /// <returns></returns>
        public GridBuilder<TModel> Filterable(Action<FilterBuilderFactory<TModel>> filters)
        {
            var factory = new FilterBuilderFactory<TModel>();
            filters.Invoke(factory);
            filter = factory.GetResult();
            DeserializeFilter();
            return this.Filterable();
        }

        /// <summary>
        /// Enable Groupable feature.
        /// </summary>
        /// <returns></returns>
        public GridBuilder<TModel> Groupable()
        {
            Component.Groupable = true;
            return this;
        }

        public GridBuilder<TModel> Groupable(Action<GridColumnRefFactory<TModel>> groups)
        {
            Component.Groupable = true;
            var factory = new GridColumnRefFactory<TModel>(Component);
            groups.Invoke(factory);
            return this;
        }

        public GridBuilder<TModel> ClientBind()
        {
            return this.ClientBind(GenerateServerDataSourceUrl(), _httpMethod);
        }

        /// <summary>
        /// Specified whether the grid fixed height and with then show the scrollbar in grid
        /// </summary>
        /// <returns></returns>
        public GridBuilder<TModel> ShowScrollBar()
        {
            return this.ShowScrollBar(true);
        }

        /// <summary>
        /// Specified whether the grid fixed height and with then show the scrollbar in grid
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public GridBuilder<TModel> ShowScrollBar(bool value)
        {
            Options(opts => { opts.ShowScrollBar = value; });
            Component.ShowScrollBar = value;
            return this;
        }

        public GridBuilder<TModel> ClientBind(string url)
        {
            return this.ClientBind(url, _httpMethod);
        }

        public GridBuilder<TModel> ClientBind(string url, string httpMethod)
        {
            if (!string.IsNullOrEmpty(httpMethod))
                _httpMethod = httpMethod;
            _url = url;
            isClientMode = true;
            return this;
        }

        public GridBuilder<TModel> ServerBind(IEnumerable<TModel> model)
        {
            Component.Model = model;
            return this;
        }

        public GridBuilder<TModel> ClientDetailView(Action<ClientDetailViewBuilder<TModel>> detailView)
        {
            Component.ClientDetailView = new ClientDetailView();
            var builder = new ClientDetailViewBuilder<TModel>(Component.ClientDetailView);
            detailView.Invoke(builder);
            return this;
        }

        /// <summary>
        /// Specified the detail view of data item.
        /// </summary>
        /// <param name="detail"></param>
        /// <returns></returns>
        public GridBuilder<TModel> DetailView(Action<TModel> detail)
        {
            Component.DetailView.Content = new Action<object>(obj =>
            {
                detail.Invoke((TModel)obj);
            });
            return this;
        }

        /// <summary>
        /// Specified the detail view of data item.
        /// </summary>
        /// <param name="detail"></param>
        /// <returns></returns>
        public GridBuilder<TModel> DetailView(Func<TModel,object> detail)
        {
            Component.DetailView.InlineContent = new Func<object,object>(obj =>
            {
                return detail.Invoke((TModel)obj);
            });
            return this;
        }

        /// <summary>
        /// Auto generate columns from binding data model
        /// </summary>
        /// <returns></returns>
        public GridBuilder<TModel> AutoGenerateColumns()
        {
            return this.AutoGenerateColumns(true);
        }

        /// <summary>
        /// Specified whether auto generate columns from binding data model
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public GridBuilder<TModel> AutoGenerateColumns(bool value)
        {
            Component.AutoGenerateColumns = value;
            if (value)
            {
                var factory = new GridColumnBuilderFactory(Component, this.ViewContext, this.ViewDataContainer);
                Component.Columns.Clear();
                Type modelType = typeof(TModel);
                if (modelType != null)
                {
                    var properties = modelType.GetProperties();
                    foreach (var property in properties)
                    {
                        if (property.CanRead)
                        {
                            var express = modelType.Name + "." + property.Name;
                            var col = new GridColumn<TModel, object>(ModelMetadata.FromStringExpression(property.Name, new ViewDataDictionary<TModel>()));
                            col.Width = 100;
                            Component.AddItem(col);
                        }
                    }
                }
            }
            return this;
        }

        //public GridBuilder<TModel> ShowHorizontalScrollBar()
        //{
        //    Component.ShowHorizontalScrollBar = true;
        //    return this;
        //}

        public GridBuilder<TModel> Columns(Action<GridColumnBuilderFactory<TModel>> columns)
        {
            var factory = new GridColumnBuilderFactory<TModel>(Component, this.ViewContext, this.ViewDataContainer);
            columns.Invoke(factory);
            return this;
        }

        public GridBuilder<TModel> RowBody(Action<object> body)
        {
            Component.RowTemplate = body;
            return this;
        }

        public override void Render()
        {
            if (!isClientMode)
            {

                LoadStateFromQueryString();
                var _remoteUrl = GenerateServerDataSourceUrl();

                if (Component.Sortable)
                {
                    Options(opts =>
                    {
                        opts.OnSort = "location=\"" + _remoteUrl + (_remoteUrl.IndexOf("?") > -1 ? "&" : "?") + "orderby=\"+ui.field+\"~\"+ui.order";
                        if (Component.Pagable)
                            opts.OnSort += "+\"&index=" + _index.ToString() + "&size=" + _size.ToString() + "\"";

                        if (!string.IsNullOrEmpty(filter))
                            opts.OnSort += "+\"&filter=" + filter + "\"";

                        if (Component.Groupable)
                        {
                            if (groups != null)
                                opts.OnSort += "+\"&groupby=" + string.Join("-", groups) + "\"";
                        }

                        opts.OnSort += ";";
                    });
                }

                if (Component.Filterable)
                {
                    Options(opts =>
                    {
                        opts.OnFilterChanged = "location=\"" + _remoteUrl + (_remoteUrl.IndexOf("?") > -1 ? "&" : "?") + "filter=\"+ui.value";

                        if (Component.Groupable)
                        {
                            if (groups != null)
                                opts.OnFilterChanged += "+\"&groupby=" + string.Join("-", groups) + "\"";
                        }
                        opts.OnFilterChanged += ";";
                    });
                }

                if (Component.Groupable)
                {
                    string groupScript = "location=\"" + _remoteUrl + (_remoteUrl.IndexOf("?") > -1 ? "&" : "?") + "groupby=\"+ui.groups.join(\"-\")";

                    if (Component.Sortable)
                        groupScript += "+\"&orderby=" + GenerateQueryStringForSorting() + "\"";

                    if (Component.Pagable)
                        groupScript += "+\"&index=" + _index.ToString() + "&size=" + _size.ToString() + "\"";

                    if (!string.IsNullOrEmpty(filter))
                        groupScript += "+\"&filter=" + filter + "\"";

                    groupScript += ";";

                    Options(opts =>
                    {
                        opts.OnAddGroup = groupScript;
                        opts.OnRemoveGroup = groupScript;
                    });
                }

                if (Component.Pagable) BuildServerPager();
            }
            else
            {
                BuildClientPager();
                /*------------------------------------------------*/
                if (Component.Sortable)
                {
                    var fScripts = new StringBuilder();
                    fScripts.Append(jQuerySelector)
                       .Append(".dgrid(\"load\",\"")
                           .Append(_url)
                           .Append("\",{orderby:")
                           .Append(jQuerySelector)
                           .Append(".dgrid(\"getOrders\")");

                    if (Component.Groupable)
                        fScripts.Append(",groupby:$(\"#")
                               .Append(Component.Id)
                               .Append("\").dgrid(\"getGroups\").join(\"-\")");

                    if (Component.Pagable)
                    {
                        fScripts.Append(",index:$(\"#")
                               .Append(Component.Id)
                               .Append("_pager\").pager(\"option\",\"pageindex\")")
                               .Append(",size:$(\"#")
                               .Append(Component.Id)
                               .Append("_pager\").pager(\"option\",\"pagesize\")");
                    }

                    if (Component.Filterable)
                        fScripts.Append(",filter:")
                            .Append(jQuerySelector)
                            .Append(".dgrid(\"getFilters\")");

                    fScripts.Append("},\"").Append(Component.Id);

                    if (Component.Groupable)
                        fScripts.Append("_group_tmpl\"");
                    else
                        fScripts.Append("_tmpl\"");

                    fScripts.Append(",\"")
                    .Append(_httpMethod)
                    .Append("\");");
                    //.Append("\",")
                    //.Append(GeneratePagerReloadScripts())
                    //.Append(");");

                    Options(opts =>
                    {
                        opts.OnColumnClick = fScripts.ToString();
                    });
                }

                if (Component.Filterable)
                {
                    var fScripts = new StringBuilder();
                    fScripts.Append(jQuerySelector)
                       .Append(".dgrid(\"load\",\"")
                           .Append(_url)
                           .Append("\",{filter:")
                           .Append(jQuerySelector)
                           .Append(".dgrid(\"getFilters\")");

                    if (Component.Groupable)
                        fScripts.Append(",groupby:$(\"#")
                               .Append(Component.Id)
                               .Append("\").dgrid(\"getGroups\").join(\"-\")");

                    if (Component.Pagable)
                    {
                        fScripts.Append(",index:1")
                               .Append(",size:$(\"#")
                               .Append(Component.Id)
                               .Append("_pager\").pager(\"option\",\"pagesize\")");
                    }

                    if (Component.Sortable)
                        fScripts.Append(",orderby:")
                             .Append(jQuerySelector)
                            .Append(".dgrid(\"getOrders\")");

                    fScripts.Append("},\"").Append(Component.Id);

                    if (Component.Groupable)
                        fScripts.Append("_group_tmpl\"");
                    else
                        fScripts.Append("_tmpl\"");

                    fScripts.Append(",\"")
                    .Append(_httpMethod)
                    .Append("\",function(data){ $(\"#" + Component.Id + "_pager\").pager(\"reset\", data.Total);} );");
                    //.Append("\",")
                    //.Append(GeneratePagerReloadScripts())
                    //.Append(");");

                    Options(opts =>
                    {
                        opts.OnFilterChanged = fScripts.ToString();
                    });
                }

                /*------------Build group by scripts------------*/
                if (Component.Groupable)
                {
                    var gScripts = new StringBuilder();
                    gScripts.Append(jQuerySelector)
                       .Append(".dgrid(\"load\",\"")
                           .Append(_url)
                           .Append("\",{groupby:ui.groups.join(\"-\") ")
                           .Append(",index:$(\"#")
                           .Append(Component.Id)
                           .Append("_pager\").pager(\"option\",\"pageindex\")")
                           .Append(",size:$(\"#")
                           .Append(Component.Id)
                           .Append("_pager\").pager(\"option\",\"pagesize\")");

                    if (Component.Sortable)
                        gScripts.Append(",orderby:")
                             .Append(jQuerySelector)
                            .Append(".dgrid(\"getOrders\")");

                    if (Component.Filterable)
                        gScripts.Append(",filter:")
                            .Append(jQuerySelector)
                            .Append(".dgrid(\"getFilters\")");

                    gScripts.Append("},\"")
                           .Append(Component.Id)
                           .Append("_group_tmpl\",\"")
                           .Append(_httpMethod)
                           .Append("\");");
                    //.Append("\",")
                    //.Append(GeneratePagerReloadScripts())
                    //.Append(");");

                    Options(opts =>
                    {
                        opts.OnAddGroup = gScripts.ToString();
                        opts.OnRemoveGroup = gScripts.ToString();
                    });
                }
            }

            base.Render();

            //Register filters
            if (Component.Filterable)
                Helper.RegisterStartupScript(jQuerySelector + ".dgrid(\"setFilters\",\"" + filter + "\")");

            if (isClientMode)
                RenderAjaxGridStartupScripts(new HtmlTextWriter(this.ViewContext.Writer));

        }

        //private string GeneratePagerReloadScripts()
        //{
        //    var scripts = new StringBuilder();
        //    string pagerSelector = "$(\"#" + Component.Id + "_pager\")";

        //    scripts.Append("function(data){")
        //             .Append("var $index=")
        //             .Append(pagerSelector)
        //             .Append(".pager(\"option\",\"pageindex\"),$total=data.Total,$size=")
        //             .Append(pagerSelector)
        //             .AppendLine(".pager(\"option\",\"pagesize\");")
        //             .Append(pagerSelector)
        //             .AppendLine(".html(\"\");")
        //             .Append(pagerSelector)
        //             .AppendLine(".pager({ pageindex:$index,pagesize:$size,totalrecords:$total,changed:function(event,ui) {")
        //             .Append(jQuerySelector)
        //             .Append(".dgrid(\"load\",\"")
        //             .Append(_url)
        //             .Append("\",{index:ui.pageIndex,size:ui.pageSize");

        //    if (Component.Sortable)
        //        scripts.Append(",orderby:")
        //                 .Append(jQuerySelector)
        //                 .Append(".dgrid(\"getOrders\")");

        //    if (Component.Filterable)
        //        scripts.Append(",filter:")
        //                 .Append(jQuerySelector)
        //                 .Append(".dgrid(\"getFilters\")");

        //    if (Component.Groupable)
        //        scripts.Append(",groupby: $(\"#")
        //                 .Append(Component.Id)
        //                 .Append("\").dgrid(\"getGroups\").join(\"-\")");

        //    scripts.Append("});  }  });  }");
        //    return scripts.ToString();
        //}

        private void RenderAjaxGridStartupScripts(HtmlTextWriter writer)
        {
            if (!string.IsNullOrEmpty(_url) && (Component.Model == null))
            {
                RenderClientTemplate(writer);
                if (Component.Groupable)
                    RenderClientGroupTemplate(writer);

                var startup = new StringBuilder();
                startup.Append(jQuerySelector)
                           .Append(".dgrid(\"load\",\"")
                           .Append(_url)
                           .Append("\",{index:")
                           .Append(this._index.ToString())
                           .Append(",size:")
                           .Append(this._size.ToString());

                if (Component.GroupColumns.Count > 0)
                    startup.Append(",groupby:\"" + string.Join("-", Component.GroupColumns.Select(c => c.Name).ToArray()) + "\"");

                if ((Component.Filterable) && (!string.IsNullOrEmpty(filter)))
                    startup.Append(",filter:\"" + filter + "\"");

                if ((Component.Sortable) && (Component.OrderedColumns.Count > 0))
                    startup.Append(",orderby:\"" + GenerateQueryStringForSorting() + "\"");

                startup.Append("},\"").Append(Component.Id);

                if (Component.GroupColumns.Count > 0)
                    startup.Append("_group");

                startup.Append("_tmpl\",\"")
                           .Append(_httpMethod)
                           .Append("\",function(data){")
                           .Append("$(\"#")
                           .Append(Component.Id)
                           .Append("_pager\").pager({")
                           .Append("pageindex:")
                           .Append(this._index.ToString())
                           .Append(",pagesize:")
                           .Append(this._size.ToString())
                           .Append(",totalrecords:data.Total,changed:function(event,ui){")
                           .Append(jQuerySelector)
                           .Append(".dgrid(\"load\",\"")
                           .Append(_url)
                           .Append("\",{index:ui.pageIndex,size:ui.pageSize");

                if (Component.Sortable)
                    startup.Append(",orderby:")
                         .Append(jQuerySelector)
                        .Append(".dgrid(\"getOrders\")");

                if (Component.Filterable)
                    startup.Append(",filter:")
                        .Append(jQuerySelector)
                        .Append(".dgrid(\"getFilters\")");

                if (Component.Groupable)
                    startup.Append(",groupby: $(\"#")
                               .Append(Component.Id)
                               .Append("\").dgrid(\"getGroups\").join(\"-\")");

                startup.Append("},\"")
                .Append(Component.Id);

                if (Component.Groupable)
                    startup.Append("_group");

                startup.Append("_tmpl\",\"")
                          .Append(_httpMethod)
                          .Append("\");")
                          .Append("}")
                          .Append("});")
                          .Append("});");

                Helper.RegisterStartupScript(startup.ToString());
            }
        }

        private void BuildServerPager()
        {
            Component.Footer.Content = new Action(() =>
            {
                Helper.Dna().Pager(Component.Id + "_pager")
                    .Options(opts =>
                    {
                        string _remoteUrl = GenerateServerDataSourceUrl();
                        opts.PageIndex = _index;
                        opts.PageSize = _size;
                        opts.TotalRecords = this.ViewContext.ViewData["totalRecords"] != null ? (int)this.ViewContext.ViewData["totalRecords"] : 0;
                        opts.OnPageIndexChanged = "location=\"" + _remoteUrl + (_remoteUrl.IndexOf("?") > -1 ? "&" : "?") +
                            "index=\"+ui.pageIndex+\"&size=\"+ui.pageSize";

                        if (!string.IsNullOrEmpty(filter))
                            opts.OnPageIndexChanged += "+\"&filter=" + filter + "\"";

                        if (Component.OrderedColumns.Count > 0)
                            opts.OnPageIndexChanged += "+\"&orderby=" + GenerateQueryStringForSorting() + "\"";

                        if (Component.Groupable)
                        {
                            if (groups != null)
                                opts.OnPageIndexChanged += "+\"&groupby=" + string.Join("-", groups) + "\"";
                        }

                        opts.OnPageIndexChanged += ";";
                    })
                    .Render();
            });
        }

        private void BuildClientPager()
        {
            var url = new UrlHelper(this.ViewContext.RequestContext);
            Component.Footer.Content = new Action(() =>
            {
                this.ViewContext.Writer.Write("<div id=\"" + Component.Id + "_pager\"></div>");
            });
        }

        private string GenerateServerDataSourceUrl()
        {
            var url = new UrlHelper(this.ViewContext.RequestContext);
            string _remoteUrl = url.Action(this.ViewContext.RouteData.Values["action"].ToString(),
        this.ViewContext.RouteData.Values["controller"].ToString(),
        this.ViewContext.RequestContext.RouteData.Values);
            return _remoteUrl;
        }

        private string GenerateQueryStringForSorting()
        {
            if (Component.OrderedColumns.Count > 0)
            {
                string _ordered = "";
                var _os = new List<string>();
                foreach (var col in Component.OrderedColumns)
                {
                    if (col.Order == SortingOrders.Desc)
                        _os.Add(col.Name + "~desc");
                    else
                        _os.Add(col.Name + "~asc");
                }
                _ordered += string.Join("-", _os.ToArray());
                return _ordered;
            }
            return "";
        }

        private void LoadStateFromQueryString()
        {
            var queryString = this.ViewContext.RequestContext.HttpContext.Request.QueryString;
            if (queryString.AllKeys.Contains("index")) _index = int.Parse(queryString["index"]);
            if (queryString.AllKeys.Contains("size")) _size = int.Parse(queryString["size"]);
            if (queryString.AllKeys.Contains("filter"))
            {
                filter = queryString["filter"];
                if (!string.IsNullOrEmpty(filter))
                    DeserializeFilter();
            }

            if (queryString.AllKeys.Contains("orderby"))
            {
                var orderedCols = queryString["orderby"].Split('-');
                Component.OrderedColumns.Clear();
                Sortable(cols =>
                {
                    foreach (var orderCol in orderedCols)
                    {
                        var _args = orderCol.Split('~');
                        SortingOrders direction = SortingOrders.Asc;
                        if (_args.Count() == 2)
                            direction = string.IsNullOrEmpty(_args[1]) ? SortingOrders.Asc : (_args[1].Equals("asc", StringComparison.OrdinalIgnoreCase) ? SortingOrders.Asc : SortingOrders.Desc);
                        cols.Add(_args[0], direction);
                    }
                });
            }

            if (queryString.AllKeys.Contains("groupby"))
            {
                if (!string.IsNullOrEmpty(queryString["groupby"]))
                {
                    groups = queryString["groupby"].Split('-');
                    Component.GroupColumns.Clear();
                    Groupable(gs =>
                    {
                        foreach (var group in groups)
                            gs.Add(group);
                    });
                }
            }
        }

        private void DeserializeFilter()
        {
            var filters = filter.Split('-');
            for (int i = 0; i < filters.Length; i++)
            {
                var expr = filters[i].Split('~');
                var fieldName = expr[0];
                var filterCol = Component.Columns.FirstOrDefault(c => c.DataField.Equals(fieldName, StringComparison.OrdinalIgnoreCase));
                if (filterCol != null)
                {
                    filterCol.FilterOperator = expr[1];
                    filterCol.FilterTerm = expr[2];
                }
            }
        }

        protected void RenderClientGroupTemplate(HtmlTextWriter writer)
        {
            writer.WriteBeginTag("script");
            writer.WriteAttribute("id", Component.Id + "_group_tmpl");
            writer.WriteAttribute("type", "text/x-jquery-tmpl");
            writer.Write(HtmlTextWriter.TagRightChar);
            writer.Write("{{if Items}}");
            writer.WriteBeginTag("tr");
            writer.WriteAttribute("class", "d-grid-grouping-row");
            writer.WriteAttribute("level", "${Level}");
            writer.Write(HtmlTextWriter.TagRightChar);
            writer.WriteBeginTag("td");
            writer.WriteAttribute("class", "d-grid-cell d-grid-grouping-cell");
            writer.Write(HtmlTextWriter.TagRightChar);

            writer.WriteBeginTag("a");
            writer.WriteAttribute("class", "d-grid-group-holder");
            writer.WriteAttribute("href", "javascript:void(0);");
            writer.Write(HtmlTextWriter.TagRightChar);

            writer.WriteBeginTag("span");
            writer.WriteAttribute("class", "d-grid-group-button");
            writer.Write(HtmlTextWriter.TagRightChar);
            writer.WriteEndTag("span");
            writer.Write("${Key} (${Count})");
            writer.WriteEndTag("a");
            writer.WriteEndTag("td");
            writer.WriteEndTag("tr");

            writer.Write("{{if HasSubgroups}}");
            writer.Write("{{tmpl(Items) \"#" + Component.Id + "_group_tmpl\"}}");
            writer.Write("{{else}}");
            writer.Write("{{tmpl(Items) \"#" + Component.Id + "_tmpl\"}}");
            writer.Write("{{/if}}");
            writer.Write(" {{else}}");
            writer.Write("{{tmpl \"#" + Component.Id + "_tmpl\"}}");
            writer.Write("{{/if}}");
            writer.WriteEndTag("script");
        }

        protected void RenderClientTemplate(HtmlTextWriter writer)
        {
            writer.WriteBeginTag("script");
            writer.WriteAttribute("id", Component.Id + "_tmpl");
            writer.WriteAttribute("type", "text/x-jquery-tmpl");
            writer.Write(HtmlTextWriter.TagRightChar);
            writer.WriteBeginTag("tr");
            writer.WriteAttribute("class", "d-grid-row");
            writer.Write(HtmlTextWriter.TagRightChar);

            if (Component.ClientDetailView != null)
            {
                writer.WriteBeginTag("td");
                writer.WriteAttribute("class", "d-grid-cell d-grid-detail-collapse");
                writer.WriteAttribute("scope", "col");
                writer.WriteAttribute("style", "width:20px;");
                writer.Write(HtmlTextWriter.TagRightChar);
                writer.WriteBeginTag("span");
                writer.WriteAttribute("class", "d-grid-detail-button");
                writer.Write(HtmlTextWriter.TagRightChar);
                writer.WriteBeginTag("a");
                writer.WriteAttribute("href", Component.ClientDetailView.GetClientRequestUrl());
                writer.WriteAttribute("style", "display:none;");
                writer.Write(HtmlTextWriter.TagRightChar);
                writer.WriteEndTag("a");
                writer.WriteEndTag("span");
                writer.WriteEndTag("td");
            }

            foreach (var col in Component.Columns)
            {
                writer.WriteBeginTag("td");
                writer.WriteAttribute("class", "d-grid-cell " + col.Id.ToString());
                writer.WriteAttribute("scope", "col");
                //if (col.Width > 0)
                //    writer.WriteAttribute("style", "width:" + col.Width.ToString() + "px;");
                writer.Write(HtmlTextWriter.TagRightChar);

                if (col.ClientCellTemplate.IsEmpty)
                    writer.Write("${" + col.DataField + "}");
                else
                {
                    col.ClientCellTemplate.WriteTo(writer);
                    //if (col.ClientCellTemplate != null)
                    //    col.ClientCellTemplate.Invoke();
                    //else
                    //{
                    //    if (col.ClientCellInlineTemplate != null)
                    //        writer.Write(col.ClientCellInlineTemplate(null).ToString());
                    //}
                }
                writer.WriteEndTag("td");
            }

            writer.WriteEndTag("tr");

            if (Component.ClientDetailView != null)
            {
                writer.WriteBeginTag("tr");
                writer.WriteAttribute("class", "d-grid-row-detail");
                writer.WriteAttribute("style", "display:none;");
                writer.Write(HtmlTextWriter.TagRightChar);
                writer.Write("<td style=\"width:20px\"></td>");
                writer.WriteBeginTag("td");
                writer.WriteAttribute("class", "d-grid-detail");
                writer.WriteAttribute("colspan", Component.Columns.Count.ToString());
                writer.Write(HtmlTextWriter.TagRightChar);
                writer.WriteEndTag("td");
                writer.WriteEndTag("tr");
            }
            writer.WriteEndTag("script");
        }
    }
}

